package com.chenfan.process.pattern.template;

import com.alibaba.fastjson.JSONObject;
import com.chenfan.ccp.common.exception.ApiException;
import com.chenfan.ccp.entity.BaseEntity;
import com.chenfan.ccp.util.tool.UuidUtil;
import com.chenfan.process.common.constants.ProcessConstants;
import com.chenfan.process.common.exception.ProcessResultCode;
import com.chenfan.process.entity.dto.DetailAndProcessInstanceDTO;
import com.chenfan.process.entity.po.ProcessDetailPo;
import com.chenfan.process.entity.po.ProcessInstanceApprovePo;
import com.chenfan.process.entity.po.ProcessRelatedAttribute;
import com.chenfan.process.entity.po.ProcessRelatedPo;
import com.chenfan.process.enums.LinkedTypeEnums;
import com.chenfan.process.enums.ProcessResultTypeEnums;
import com.chenfan.process.enums.SignTypeEnum;
import com.chenfan.process.util.CalculateUtil;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author K
 * @date 2022/7/12 1:43 PM
 */
@Component
@Scope("prototype")
public class ComplexNodeHandler extends AbstractNodeHandler {

    @Override
    public DetailAndProcessInstanceDTO getDetailAndProcessInstances() {
        Map<Long, List<ProcessRelatedPo>> groupMap = processRelatedPoList.stream().collect(Collectors.groupingBy(ProcessRelatedPo::getGroupId));

        if (groupMap.size() > 1) {
            return optionalHandler();
        }

        boolean typeMatch = processRelatedPoList.stream().anyMatch(e -> Objects.equals(e.getType(), LinkedTypeEnums.PARALLEL.getCode()));
        boolean linkTypeMatch = processRelatedPoList.stream().anyMatch(e -> Objects.equals(e.getLinkType(), LinkedTypeEnums.PARALLEL.getCode()));

        if (typeMatch || linkTypeMatch) {
            return multiple(processRelatedPoList, ProcessConstants.OR_SIGN_NODE, SignTypeEnum.OR_SIGN.getCode());
        } else {
            return multiple(processRelatedPoList, ProcessConstants.COUNTER_SIGN_NODE, SignTypeEnum.COUNTER_SIGN.getCode());
        }
    }

    @Override
    public boolean processBefore() {
        return updateBefore();
    }

    private DetailAndProcessInstanceDTO optionalHandler() {
        // 填充0,防止分组报错. [条件分支上出现某一遍为普通, 另一边为多参数]
        processRelatedPoList.forEach(e -> {
            if (Objects.isNull(e.getGroupId())) {
                e.setGroupId(0L);
            }
        });
        List<Long> idList = processRelatedPoList.stream().map(BaseEntity::getId).collect(Collectors.toList());
        Map<Long, List<ProcessRelatedPo>> groupMap = processRelatedPoList.stream().collect(Collectors.groupingBy(ProcessRelatedPo::getGroupId));
        Map<Long, List<ProcessRelatedAttribute>> relatedMap = processRelatedAttributeService.listByRelatedId(idList)
                .stream().collect(Collectors.groupingBy(ProcessRelatedAttribute::getRelatedId));
        for (Map.Entry<Long, List<ProcessRelatedPo>> entry : groupMap.entrySet()) {
            List<ProcessRelatedPo> processRelatedPos = entry.getValue();
            ProcessRelatedPo related = processRelatedPos.get(0);
            List<ProcessRelatedAttribute> processRelatedAttributeList = relatedMap.getOrDefault(related.getId(), Collections.emptyList());
            JSONObject jsonObject = CalculateUtil.checkField(processRelatedAttributeList, checkField);
            boolean match = CalculateUtil.match(processRelatedAttributeList, jsonObject);
            if (match) {
                if (processRelatedPos.size() > 1) {
                    // 如果为多人审批的情况.
                    if (related.orSign()) {
                        return multiple(processRelatedPos, ProcessConstants.OR_SIGN_NODE, SignTypeEnum.OR_SIGN.getCode());
                    } else {
                        return multiple(processRelatedPos, ProcessConstants.COUNTER_SIGN_NODE, SignTypeEnum.COUNTER_SIGN.getCode());
                    }
                } else {
                    // normal situation
                    return single(processRelatedPos, 0);
                }

            }
        }
        throw new ApiException(ProcessResultCode.OPTION_ERROR);
    }

    public DetailAndProcessInstanceDTO single(List<ProcessRelatedPo> processRelatedPos, Integer signType) {
        ProcessRelatedPo related = processRelatedPos.get(0);

        ProcessDetailPo processDetail = ProcessDetailPo.builder()
                .processId(related.getProcessBeforeId()).nextProcessId(related.getProcessAfterId())
                .groupId(related.getGroupId())
                .resultType(ProcessResultTypeEnums.CREATED.getIndex())
                .approvalTemplateId(approvalPo.getApprovalTemplateId()).parentId(approvalPo.getApprovalId()).build();
        processDetail.setId(UuidUtil.generateId());

        relatedList.addAll(processRelatedPos);
        List<ProcessInstanceApprovePo> processInstanceList = getUserIdList(processRelatedPos, signType, processDetail.getId());
        return DetailAndProcessInstanceDTO.builder().processDetail(processDetail).processInstanceList(processInstanceList).build();
    }

    public DetailAndProcessInstanceDTO multiple(List<ProcessRelatedPo> processRelatedPos, Long nextProcessId, Integer signType) {
        ProcessRelatedPo related = processRelatedPos.get(0);

        ProcessDetailPo processDetail = ProcessDetailPo.builder()
                .processId(related.getProcessBeforeId()).nextProcessId(nextProcessId)
                .groupId(related.getGroupId())
                .resultType(ProcessResultTypeEnums.CREATED.getIndex())
                .approvalTemplateId(approvalPo.getApprovalTemplateId()).parentId(approvalPo.getApprovalId()).build();
        processDetail.setId(UuidUtil.generateId());

        relatedList.addAll(processRelatedPos);
        List<ProcessInstanceApprovePo> processInstanceList = getUserIdList(processRelatedPos, signType, processDetail.getId());
        return DetailAndProcessInstanceDTO.builder().processDetail(processDetail).processInstanceList(processInstanceList).build();
    }

    @Override
    public DetailAndProcessInstanceDTO generateForCurrent() {
        return generateCurrent();
    }
}
